/*
 * Copyright 2012 Alan Burlison, alan@bleaklow.com.  All rights reserved.
 * Use is subject to license terms.  See LICENSE.txt for details.
 */

#include <stdlib.h>
#include "nRF24L01plus.h"
#include <stdio.h>

// Enable SPI, SPI master, SPI mode 0, double speed.
#define SPCR_VAL BIT(SPE) | BIT(MSTR)
#define SPSR_VAL BIT(SPI2X)

nRF24L01plus::nRF24L01plus(const pin_t *const _csn, const pin_t *const _ce) :
  SPIMaster(SPCR_VAL, SPSR_VAL, _csn),
  ce(_ce),
  intr(NULL) {
}

nRF24L01plus::nRF24L01plus(const pin_t *const _csn, const pin_t *const _ce,
      const pin_t *const _intr) :
  SPIMaster(SPCR_VAL, SPSR_VAL, _csn),
  ce(_ce),
  intr(_intr) {
}

void nRF24L01plus::initPins(bool shared) {
    SPIInitPins(shared);
    SET_BIT_HI(*ce->ddr, ce->bit);      // Output.
    SET_BIT_LO(*ce->port, ce->bit);     // Low.
    if (intr != NULL) {
        SET_BIT_LO(*intr->ddr, intr->bit);      // Input.
        SET_BIT_LO(*intr->port, intr->bit);     // No pullup.
    }
}

uint8_t nRF24L01plus::r_register(uint8_t reg) {
    SPISelect();
    SPIWriteRead(R_REGISTER | (reg & REG_MASK));
    uint8_t value = SPIWriteRead(NOP);
    SPIDeselect();
    return value;
}

uint8_t nRF24L01plus::r_register(uint8_t reg, uint8_t *value, uint8_t len) {
    SPISelect();
    uint8_t status = SPIWriteRead(R_REGISTER | (reg & REG_MASK));
    while (len-- > 0) {
        *value++ = SPIWriteRead(NOP);
    }
    SPIDeselect();
    return status;
}

uint8_t nRF24L01plus::w_register(uint8_t reg, uint8_t value) {
    SPISelect();
    uint8_t status = SPIWriteRead(W_REGISTER | (reg & REG_MASK));
    SPIWriteRead(value);
    SPIDeselect();
    return status;
}

uint8_t nRF24L01plus::w_register(uint8_t reg,
  const uint8_t *value, uint8_t len) {
    SPISelect();
    uint8_t status = SPIWriteRead(W_REGISTER | (reg & REG_MASK));
    while (len-- > 0) {
        SPIWriteRead(*value++);
    }
    SPIDeselect();
    return status;
}

uint8_t nRF24L01plus::r_rx_payload(uint8_t *value, uint8_t len) {
    SPISelect();
    uint8_t status = SPIWriteRead(R_RX_PAYLOAD);
    while (len-- > 0) {
        *value++ = SPIWriteRead(NOP);
    }
    SPIDeselect();
    return status;
}

uint8_t nRF24L01plus::flush_tx() {
    SPISelect();
    uint8_t status = SPIWriteRead(FLUSH_TX);
    SPIDeselect();
    return status;
}

uint8_t nRF24L01plus::flush_rx() {
    SPISelect();
    uint8_t status = SPIWriteRead(FLUSH_RX);
    SPIDeselect();
    return status;
}

uint8_t nRF24L01plus::w_tx_payload(const uint8_t *value, uint8_t len) {
    SPISelect();
    uint8_t status = SPIWriteRead(W_TX_PAYLOAD);
    uint8_t i = 0;
    while (i < len) {
        SPIWriteRead(*value++);
        i++;
    }
    while (i < msgLen) {
        SPIWriteRead(0x00);
        i++;
    }
    SPIDeselect();
    return status;
}

uint8_t nRF24L01plus::reuse_tx_pl() {
    SPISelect();
    uint8_t status = SPIWriteRead(REUSE_TX_PL);
    SPIDeselect();
    return status;
}

uint8_t nRF24L01plus::r_rx_pl_wid() {
    SPISelect();
    uint8_t width = SPIWriteRead(R_RX_PL_WID);
    SPIDeselect();
    return width;
}

uint8_t nRF24L01plus::w_ack_payload(uint8_t pipe,
  const uint8_t *value, uint8_t len) {
    SPISelect();
    uint8_t status = SPIWriteRead(W_ACK_PAYLOAD | (pipe & ACK_PL_MASK));
    uint8_t i = 0;
    while (i < len) {
        SPIWriteRead(*value++);
        i++;
    }
    while (i < msgLen) {
        SPIWriteRead(0x00);
        i++;
    }
    SPIDeselect();
    return status;
}

uint8_t nRF24L01plus::w_tx_payload_no_ack(const uint8_t *value, uint8_t len) {
    SPISelect();
    uint8_t status = SPIWriteRead(W_TX_PAYLOAD_NO_ACK);
    uint8_t i = 0;
    while (i < len) {
        SPIWriteRead(*value++);
        i++;
    }
    while (i < msgLen) {
        SPIWriteRead(0x00);
        i++;
    }
    SPIDeselect();
    return status;
}

uint8_t nRF24L01plus::nop() {
    SPISelect();
    uint8_t status = SPIWriteRead(NOP);
    SPIDeselect();
    return status;
}

void nRF24L01plus::setMode(nRF24L01plus::Mode mode, uint8_t chan,
  const uint8_t *const addr, uint8_t addrLen, uint8_t _msgLen) {
    switch (mode) {
    case Disabled:
        w_register(CONFIG, 0x00);
        msgLen = 0;
        return;
    case Receive:
        w_register(CONFIG, BIT(EN_CRC) | BIT(CRCO) | BIT(PWR_UP)
          | BIT(PRIM_RX));
        break;
    case Transmit:
        w_register(CONFIG, BIT(EN_CRC) | BIT(CRCO) | BIT(PWR_UP));
        break;
    }
    msgLen =_msgLen;
    w_register(EN_AA, 0x00);
    w_register(EN_RXADDR, 0x01);
    w_register(SETUP_AW, addrLen - 2);
    w_register(SETUP_RETR, 0x00);
    w_register(RF_CH, chan);
    w_register(RF_SETUP, BIT(RF_DR_LOW) | RF_PWR_SET(3));
    w_register(RX_ADDR_P0, addr, addrLen);
    w_register(TX_ADDR, addr, addrLen);
    w_register(RX_PW_P0, msgLen);
    w_register(RX_PW_P1, 0);
    w_register(RX_PW_P2, 0);
    w_register(RX_PW_P3, 0);
    w_register(RX_PW_P4, 0);
    w_register(RX_PW_P5, 0);
    w_register(DYNPD, 0x00);
    w_register(FEATURE, 0x00);
    if (mode == Disabled) {
        SET_BIT_LO(*ce->port, ce->bit);
    } else {
        SET_BIT_HI(*ce->port, ce->bit);
    }
}

bool nRF24L01plus::txReady() {
    return ! (nop() & BIT(TX_FULL));
}

uint8_t nRF24L01plus::tx(const uint8_t *buf, uint8_t len) {
    while (nop() & BIT(TX_FULL)) {
        ; // Busy wait.
    }
    return w_tx_payload(buf, len);
}

bool nRF24L01plus::rxReady() {
    return nop() & BIT(RX_DR);
}

uint8_t nRF24L01plus::rx(uint8_t *buf, uint8_t len) {
    while (! (nop() & BIT(RX_DR))) {
        ; // Busy wait.
    }
    r_rx_payload(buf, len);
    uint8_t status, fifo;
    status = r_register(FIFO_STATUS, &fifo, sizeof(fifo));
    if (fifo & BIT(FIFO_RX_EMPTY)) {
        status = w_register(RADIO_STATUS, status | BIT(RX_DR));
    }
    return status;
}
